﻿using System;
using System.Threading;
using System.Text;
using System.Xml;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Collections.ObjectModel;
using System.Net;
using System.ServiceModel.Description;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

using System.Windows.Browser;
using System.ComponentModel.Composition;
using MEFedMVVM.ViewModelLocator;
using MEFedMVVM.Services.Contracts;
using MEFedMVVM.Common;
using System.IO;

using CRM.Tools.ODataQueryDesigner.CrmSdk;
using CRM.Tools.ODataQueryDesigner.Readers;


namespace CRM.Tools.ODataQueryDesigner
{


    [ExportViewModel("QueryDesignerViewModel")]
    public class QueryDesignerViewModel : NotifyPropertyChangedBase
    {
        #region private variables

        private SynchronizationContext _syncContext;

        private ICommand _generate_Click;
        private ICommand _execute_Click;
        private ICommand _openBrowser_Click;
        private ICommand _addGroupAND_Click;
        private ICommand _addGroupOR_Click;
        

        private ObservableCollection<EntityClass> _entities;
        private EntityClass _entity;
        private EntityClass _selectedEntity;
        private RelationshipClass _selectedOneToManyRelationship;
        private RelationshipClass _selectedManyToOneRelationship;
        private RelationshipClass _selectedManyToManyRelationship;

        private AttributeClass _selectedSortBy1Attribute;
        private AttributeClass _selectedSortBy2Attribute;
        private SortDirection _sortBy1Direction;
        private SortDirection _sortBy2Direction;
        
        private IMetadataReader _dataReader;
        private IDispatcherService _dispatcher;

        #region ODataQuery properties

        private String _ODataQuery;
        public String ResultXml { get; set; }
        public StringBuilder JSONResultXml { get; set; }
        public StringBuilder ATOMResultXml { get; set; }
        private int? _skipNext;
        private int? _top;
        public string QueryLength { get; set; }
        public bool IsAllowedToExecuteREST { get; set; }
        public bool IsAllowedToOpenBrowser { get; set; }

        public bool IsAllowedToGenerate
        {
            get { return (SelectedEntity != null); }
           
        }
       
        
        #endregion


        #endregion


        #region Constructors

        [ImportingConstructor]
        public QueryDesignerViewModel(IMetadataReader dataReader, IDispatcherService dispatcher)
        {
            //try
            //{
                _dataReader = dataReader;
                _dispatcher = dispatcher;
                ATOMResultXml = new StringBuilder();
                JSONResultXml = new StringBuilder();
                IsAllowedToExecuteREST = false;
                IsAllowedToOpenBrowser = false;
                //Keeps a reference to the UI thread
                _syncContext = SynchronizationContext.Current;

                _entities = _dataReader.GetEntities();
                _entity = new EntityClass();
                
               
            //}
            //catch (SystemException se)
            //{
            //    if (null != _syncContext)
            //    {
            //        _syncContext.Send(new SendOrPostCallback(showErrorDetails), se);
            //    }
            //}
            //catch (Exception ex)
            //{
            //    if (null != _syncContext)
            //    {
            //        _syncContext.Send(new SendOrPostCallback(showErrorDetails), ex);
            //    }
            //}


        }
        #endregion


        #region public members
                

        #region Entity Properties
        /// <summary>
        /// 
        /// </summary>
        public EntityClass Entity
        {
            get { return _entity; }
            private set
            {
                _entity = value;

                if (_entity != null)
                {
                    //                
                    // Handle Select All on attributes event
                    //
                    _entity.AllAttributesSelectedEvent += ((o, a) =>
                    {
                        foreach (var item in _entity.Attributes)
                        {
                            if (item.SchemaName != "(Select All)")
                                item.UpdateSelected = true;
                        }
                    });

                    //                
                    // Handle Select All on attributes event
                    //
                    _entity.AllAttributesUnSelectedEvent += ((o, a) =>
                    {
                        foreach (var item in _entity.Attributes)
                        {
                            if (item.SchemaName != "(Select All)")
                                item.UpdateSelected = false;
                        }
                    });

                    //                
                    // Handle Select All on attributes event
                    //
                    _entity.AnyAttributeUnSelectedEvent += ((o, a) =>
                    {
                        _entity.Attributes[0].UpdateSelected = false;

                    });

                }

                OnPropertyChanged(() => Entity);
                OnPropertyChanged(() => AttributeList);
                OnPropertyChanged(() => SortByAttributesList);
                OnPropertyChanged(() => FiltersList);
                OnPropertyChanged(() => OneToMany_RelationshipList);
                OnPropertyChanged(() => ManyToOne_RelationshipList);
                OnPropertyChanged(() => ManyToMany_RelationshipList);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public EntityClass SelectedEntity
        {
            get { return _selectedEntity; }
            set
            {
                _selectedEntity = value;


                ProcessSelectedEntityMetaData(_selectedEntity.LogicalName);

                ODataQuery = String.Format("{0}{1}{2}", ClientScript.GetServerUrl(), "/xrmservices/2011/OrganizationData.svc", BuildODataQuery());

                OnPropertyChanged(() => IsAllowedToGenerate);
                OnPropertyChanged(() => SelectedEntity);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public ObservableCollection<EntityClass> EntityList
        {
            get { return _entities; }
            private set
            {
                _entities = value;
                OnPropertyChanged(() => EntityList);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public ObservableCollection<AttributeClass> AttributeList
        {
            get { return _entity.Attributes; }
            private set
            {
                _entity.Attributes = value;
                OnPropertyChanged(() => AttributeList);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public ObservableCollection<AttributeClass> SortByAttributesList
        {
            get { return _entity.SortByAttributes; }
            private set
            {
                _entity.SortByAttributes = value;

                OnPropertyChanged(() => SortByAttributesList);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public ObservableCollection<FilterClass> FiltersList
        {
            get { return _entity.Filters; }
            private set
            {
                _entity.Filters = value;

                OnPropertyChanged(() => FiltersList);
            }
        }

        #endregion

        #region Relationship List Properties

        /// <summary>
        /// 
        /// </summary>
        public ObservableCollection<RelationshipClass> OneToMany_RelationshipList
        {
            get { return _entity.Relationships_OneToMany; }
            private set
            {
                _entity.Relationships_OneToMany = value;
                OnPropertyChanged(() => OneToMany_RelationshipList);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public ObservableCollection<RelationshipClass> ManyToOne_RelationshipList
        {
            get { return _entity.Relationships_ManyToOne; }
            private set
            {
                _entity.Relationships_ManyToOne = value;
                OnPropertyChanged(() => ManyToOne_RelationshipList);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public ObservableCollection<RelationshipClass> ManyToMany_RelationshipList
        {
            get { return _entity.Relationships_ManyToMany; }
            private set
            {
                _entity.Relationships_ManyToMany = value;
                OnPropertyChanged(() => ManyToMany_RelationshipList);
            }
        }

        #endregion


        #region Relationship Attribute Properties

        public RelationshipClass SelectedOneToManyRelationship
        {
            get { return _selectedOneToManyRelationship; }
            set
            {
                _selectedOneToManyRelationship = value;

                if (_selectedOneToManyRelationship != null && _selectedOneToManyRelationship.Entity == null)
                    _selectedOneToManyRelationship.Entity = _dataReader.GetEntity(_selectedOneToManyRelationship.EntityLogicalName, false);

                //                
                // Handle Select All on attributes event
                //
                if (_selectedOneToManyRelationship != null)
                {
                    _selectedOneToManyRelationship.Entity.AllAttributesSelectedEvent += ((o, a) =>
                    {
                        foreach (var item in _selectedOneToManyRelationship.Entity.Attributes)
                        {
                            if (item.SchemaName != "(Select All)")
                                item.UpdateSelected = true;
                        }
                    });


                    //                
                    // Handle Select All on attributes event
                    //
                    _selectedOneToManyRelationship.Entity.AllAttributesUnSelectedEvent += ((o, a) =>
                    {
                        foreach (var item in _selectedOneToManyRelationship.Entity.Attributes)
                        {
                            if (item.SchemaName != "(Select All)")
                                item.UpdateSelected = false;
                        }
                    });

                    //                
                    // Handle Select All on attributes event
                    //
                    _selectedOneToManyRelationship.Entity.AnyAttributeUnSelectedEvent += ((o, a) =>
                    {
                        _selectedOneToManyRelationship.Entity.Attributes[0].UpdateSelected = false;

                    });
                }

                OnPropertyChanged(() => SelectedOneToManyRelationship);
                OnPropertyChanged(() => OneToMany_RelationshipAttributeList);
            }
        }

        public ObservableCollection<AttributeClass> OneToMany_RelationshipAttributeList
        {
            get
            {
                if (_selectedOneToManyRelationship != null && _selectedOneToManyRelationship.Entity != null)
                    return _selectedOneToManyRelationship.Entity.Attributes;
                else
                    return null;
            }
            set
            {
                _selectedOneToManyRelationship.Entity.Attributes = value;
                OnPropertyChanged(() => OneToMany_RelationshipAttributeList);
            }
        }

        public RelationshipClass SelectedManyToOneRelationship
        {
            get { return _selectedManyToOneRelationship; }
            set
            {
                _selectedManyToOneRelationship = value;

                if (_selectedManyToOneRelationship != null && _selectedManyToOneRelationship.Entity == null)
                    _selectedManyToOneRelationship.Entity = _dataReader.GetEntity(_selectedManyToOneRelationship.EntityLogicalName, false);

                if (_selectedManyToOneRelationship != null)
                {
                    //                
                    // Handle Select All on attributes event
                    //
                    _selectedManyToOneRelationship.Entity.AllAttributesSelectedEvent += ((o, a) =>
                    {
                        foreach (var item in _selectedManyToOneRelationship.Entity.Attributes)
                        {
                            if (item.SchemaName != "(Select All)")
                                item.UpdateSelected = true;
                        }
                    });

                    //                
                    // Handle Select All on attributes event
                    //
                    _selectedManyToOneRelationship.Entity.AllAttributesUnSelectedEvent += ((o, a) =>
                    {
                        foreach (var item in _selectedManyToOneRelationship.Entity.Attributes)
                        {
                            if (item.SchemaName != "(Select All)")
                                item.UpdateSelected = false;
                        }
                    });

                    //                
                    // Handle Select All on attributes event
                    //
                    _selectedManyToOneRelationship.Entity.AnyAttributeUnSelectedEvent += ((o, a) =>
                    {
                        _selectedManyToOneRelationship.Entity.Attributes[0].UpdateSelected = false;

                    });
                }

                OnPropertyChanged(() => SelectedManyToOneRelationship);
                OnPropertyChanged(() => ManyToOne_RelationshipAttributeList);
            }
        }

        public ObservableCollection<AttributeClass> ManyToOne_RelationshipAttributeList
        {
            get
            {
                if (_selectedManyToOneRelationship != null && _selectedManyToOneRelationship.Entity != null)
                    return _selectedManyToOneRelationship.Entity.Attributes;
                else
                    return null;
            }
            set
            {
                _selectedManyToOneRelationship.Entity.Attributes = value;
                OnPropertyChanged(() => ManyToOne_RelationshipAttributeList);
            }
        }

        public RelationshipClass SelectedManyToManyRelationship
        {
            get { return _selectedManyToManyRelationship; }
            set
            {
                _selectedManyToManyRelationship = value;

                if (_selectedManyToManyRelationship != null && _selectedManyToManyRelationship.Entity == null)
                    _selectedManyToManyRelationship.Entity = _dataReader.GetEntity(_selectedManyToManyRelationship.EntityLogicalName, false);


                if (_selectedManyToManyRelationship != null)
                {
                    //                
                    // Handle Select All on attributes event
                    //
                    _selectedManyToManyRelationship.Entity.AllAttributesSelectedEvent += ((o, a) =>
                    {
                        foreach (var item in _selectedManyToManyRelationship.Entity.Attributes)
                        {
                            if (item.SchemaName != "(Select All)")
                                item.UpdateSelected = true;
                        }
                    });

                    //                
                    // Handle Select All on attributes event
                    //
                    _selectedManyToManyRelationship.Entity.AllAttributesUnSelectedEvent += ((o, a) =>
                    {
                        foreach (var item in _selectedManyToManyRelationship.Entity.Attributes)
                        {
                            if (item.SchemaName != "(Select All)")
                                item.UpdateSelected = false;
                        }
                    });

                    //                
                    // Handle Select All on attributes event
                    //
                    _selectedManyToManyRelationship.Entity.AnyAttributeUnSelectedEvent += ((o, a) =>
                    {
                        _selectedManyToManyRelationship.Entity.Attributes[0].UpdateSelected = false;

                    });
                }

                OnPropertyChanged(() => SelectedManyToManyRelationship);
                OnPropertyChanged(() => ManyToMany_RelationshipAttributeList);
            }
        }

        public ObservableCollection<AttributeClass> ManyToMany_RelationshipAttributeList
        {
            get
            {
                if (_selectedManyToManyRelationship != null && _selectedManyToManyRelationship.Entity != null)
                    return _selectedManyToManyRelationship.Entity.Attributes;
                else
                    return null;
            }
            set
            {
                _selectedManyToManyRelationship.Entity.Attributes = value;
                OnPropertyChanged(() => ManyToMany_RelationshipAttributeList);
            }
        }

        #endregion

       

        #endregion


        #region Sorting Properties

        public AttributeClass SelectedSortBy1Attribute
        {
            get { return _selectedSortBy1Attribute; }
            set { if (_selectedSortBy1Attribute != value) { _selectedSortBy1Attribute = value; OnPropertyChanged(() => SelectedSortBy1Attribute); } }
        }
        public AttributeClass SelectedSortBy2Attribute
        {
            get { return _selectedSortBy2Attribute; }
            set { if (_selectedSortBy2Attribute != value) { _selectedSortBy2Attribute = value; OnPropertyChanged(() => SelectedSortBy2Attribute); } }
        }
        public SortDirection SortBy1Direction
        {
            get { return _sortBy1Direction; }
            set { if (_sortBy1Direction != value) { _sortBy1Direction = value; OnPropertyChanged(() => SortBy1Direction); } }
        }
        public SortDirection SortBy2Direction
        {
            get { return _sortBy2Direction; }
            set { if (_sortBy2Direction != value) { _sortBy2Direction = value; OnPropertyChanged(() => SortBy2Direction); } }
        }

        #endregion


        #region OData Properties

        private void ProcessSelectedEntityMetaData(String EntityLogicalName)
        {

            Entity = _dataReader.GetEntity(EntityLogicalName, true);

        }

        private string BuildODataQuery()
        {
            // /AccountSet?$select=Name,Address1_PostalCode,account_primary_contact/FullName,contact_customer_accounts/FullName&$orderby=Name asc&$expand=account_primary_contact,contact_customer_accounts

            string query = "/" + _selectedEntity.SchemaNameSet;

            List<string> selectAttributes = new List<string>();
            List<string> expandRelationships = new List<string>();
            List<string> orderByAttributes = new List<string>();
            List<string> filterAttributes = new List<string>();


            // Process select attributes
            if (Entity.Attributes.Count > 0)
            {
                if (Entity.IsSelectAllAttributes == true)
                {
                    selectAttributes.Add("*");
                }
                else
                {
                    foreach (AttributeClass item in Entity.Attributes)
                    {
                        if (item.IsSelected == true)
                        {
                            if (item.SchemaName != "{Select All)")
                            {
                                selectAttributes.Add(item.SchemaName);
                            }
                        }
                    }
                }
            }

            // Process select attributes
            if (Entity.Relationships_ManyToOne.Count > 0)
            {

                foreach (RelationshipClass rel in Entity.Relationships_ManyToOne)
                {
                    if (rel.Entity != null && rel.Entity.Attributes != null && rel.Entity.Attributes != null && rel.Entity.Attributes.Count > 0)
                    {


                        if (rel.Entity.IsSelectAllAttributes == true)
                        {
                            selectAttributes.Add(rel.SchemaName);
                            // add related to expand
                            if (!expandRelationships.Contains(rel.SchemaName))
                                expandRelationships.Add(rel.SchemaName);
                        }
                        else
                        {
                            bool addRelToExpand = false;
                            foreach (AttributeClass item in rel.Entity.Attributes)
                            {
                                if (item.IsSelected == true)
                                {
                                    if (item.SchemaName != "{Select All)")
                                    {
                                        selectAttributes.Add(rel.SchemaName + "/" + item.SchemaName);
                                        addRelToExpand = true;
                                    }
                                }
                            }
                            if (addRelToExpand && !expandRelationships.Contains(rel.SchemaName))
                            {
                                expandRelationships.Add(rel.SchemaName);
                            }
                        }
                    }
                }
            }

            // Process select attributes
            if (Entity.Relationships_OneToMany.Count > 0)
            {

                foreach (RelationshipClass rel in Entity.Relationships_OneToMany)
                {
                    if (rel.Entity != null && rel.Entity.Attributes != null && rel.Entity.Attributes != null && rel.Entity.Attributes.Count > 0)
                    {
                        if (rel.Entity.IsSelectAllAttributes == true)
                        {
                            selectAttributes.Add(rel.SchemaName);
                            // add related to expand
                            if (!expandRelationships.Contains(rel.SchemaName))
                                expandRelationships.Add(rel.SchemaName);
                        }
                        else
                        {
                            bool addRelToExpand = false;
                            foreach (AttributeClass item in rel.Entity.Attributes)
                            {
                                if (item.IsSelected == true)
                                {
                                    if (item.SchemaName != "{Select All)")
                                    {
                                        selectAttributes.Add(rel.SchemaName + "/" + item.SchemaName);
                                        addRelToExpand = true;
                                    }
                                }
                            }
                            if (addRelToExpand && !expandRelationships.Contains(rel.SchemaName))
                            {
                                expandRelationships.Add(rel.SchemaName);
                            }
                        }
                    }
                }
            }

            // Process select attributes
            if (Entity.Relationships_ManyToMany.Count > 0)
            {

                foreach (RelationshipClass rel in Entity.Relationships_ManyToMany)
                {
                    if (rel.Entity != null && rel.Entity.Attributes != null && rel.Entity.Attributes != null && rel.Entity.Attributes.Count > 0)
                    {


                        if (rel.Entity.IsSelectAllAttributes == true)
                        {
                            selectAttributes.Add(rel.SchemaName);
                            // add related to expand
                            if (!expandRelationships.Contains(rel.SchemaName))
                                expandRelationships.Add(rel.SchemaName);
                        }
                        else
                        {
                            bool addRelToExpand = false;
                            foreach (AttributeClass item in rel.Entity.Attributes)
                            {
                                if (item.IsSelected == true)
                                {
                                    if (item.SchemaName != "{Select All)")
                                    {
                                        selectAttributes.Add(rel.SchemaName + "/" + item.SchemaName);
                                        addRelToExpand = true;
                                    }
                                }
                            }
                            if (addRelToExpand && !expandRelationships.Contains(rel.SchemaName))
                            {
                                expandRelationships.Add(rel.SchemaName);
                            }
                        }
                    }
                }
            }

            // Process order by attributes
            //
            if (SelectedSortBy1Attribute != null && !String.IsNullOrEmpty(SelectedSortBy1Attribute.SchemaName))
            {
                orderByAttributes.Add(SelectedSortBy1Attribute.SchemaName + " " + SortBy1Direction.ToString());
            }
            if (SelectedSortBy2Attribute != null && !String.IsNullOrEmpty(SelectedSortBy2Attribute.SchemaName))
            {
                orderByAttributes.Add(SelectedSortBy2Attribute.SchemaName + " " + SortBy2Direction.ToString());
            }

            // Process Filters
            //
            if (Entity.Filters.Count > 1)
            {
                string filterString = "";
                string attribute = "";

                foreach(FilterClass item in Entity.Filters)
                {
                    filterString = "";

                    if (item.SelectedAttribute != null)
                    {

                        if (item.IsPrimaryGroupAttribute)
                            filterString += "(";


                        switch(item.SelectedAttribute.AttributeType.Value)
                        {
                            case AttributeTypeCode.Picklist:
                            //case AttributeTypeCode.Double:
                            //case AttributeTypeCode.Decimal:
                            //case AttributeTypeCode.Integer:
                            case AttributeTypeCode.Money:
                            case AttributeTypeCode.State:
                            case AttributeTypeCode.Status:
                            //case AttributeTypeCode.BigInt:
                                attribute = String.Format("{0}/{1}", item.SelectedAttribute.SchemaName, "Value");
                                break;
                            case AttributeTypeCode.Lookup:
                            case AttributeTypeCode.Customer:
                            case AttributeTypeCode.Owner:
                                attribute = String.Format("{0}/{1}", item.SelectedAttribute.SchemaName, "Id");
                                break;
                            default:
                                attribute = item.SelectedAttribute.SchemaName;
                                break;
                        }

                        switch (item.FilterOperator)
                        {
                            case "Equals":
                                filterString += String.Format("{0} eq {1}", attribute, FormatValue(item));
                                break;
                            case "Does Not Equal":
                                filterString += String.Format("{0} ne {1}", attribute, FormatValue(item));
                                break;
                            case "Contains":
                                filterString += String.Format("substringof({0},{1})", FormatValue(item), attribute);
                                break;
                            case "Does Not Contain":
                                filterString += String.Format("not substringof({0},{1})", FormatValue(item), attribute);
                                break;
                            case "Begins With":
                                filterString += String.Format("startswith({0},{1})", attribute, FormatValue(item));
                                break;
                            case "Does Not Begin With":
                                filterString += String.Format("not startswith({0},{1})", attribute, FormatValue(item));
                                break;
                            case "Ends With":
                                filterString += String.Format("endswith({0},{1})", attribute, FormatValue(item));
                                break;
                            case "Does Not End With":
                                filterString += String.Format("not endswith({0},{1})", attribute, FormatValue(item));
                                break;
                            case "Is Greater Than":
                                filterString += String.Format("{0} gt {1}", attribute, FormatValue(item));
                                break;
                            case "Is Greater That or Equal To":
                            case "On Or After":
                                filterString += String.Format("{0} ge {1}", attribute, FormatValue(item));
                                break;
                            case "Is Less Than":
                                filterString += String.Format("{0} lt {1}", attribute, FormatValue(item));
                                break;
                            case "Is Less Than or Equal To":
                            case "On Or Before":
                               filterString += String.Format("{0} le {1}", attribute, FormatValue(item));
                                break;
                            case "Contains Data":
                                filterString += String.Format("{0} ne null", attribute);
                                break;
                            case "Does Not Contain Data":
                                filterString += String.Format("{0} eq null", attribute);
                                break;
                        }


                        if (item.IsFinalGroupAttribute)
                        {
                            filterString += ")";

                            if (Entity.Filters.Count > (Entity.Filters.IndexOf(item) + 1) && Entity.Filters[Entity.Filters.IndexOf(item) + 1].SelectedAttribute != null)
                                filterString += (item.GroupLogicalOperator == "AND") ? " and " : " or ";

                        }
                        else
                        {
                            if (Entity.Filters.Count > (Entity.Filters.IndexOf(item) + 1) && Entity.Filters[Entity.Filters.IndexOf(item) + 1].SelectedAttribute != null)
                                filterString += (item.LogicalOperator == "AND") ? " and " : " or ";
                        }

                        filterAttributes.Add(filterString);
                    }
                }

            }
            
            query += "?" + Helpers.ODataBuilder.BuildQuery(selectAttributes, orderByAttributes, filterAttributes, expandRelationships);

            return query;
        }

        /// <summary>
        /// Formats the value of required such as guid'' and datetime''
        /// </summary>
        /// <param name="Filter"></param>
        /// <returns></returns>
        private String FormatValue(FilterClass Filter)
        {
            String tmp = Filter.Value;

            switch (Filter.SelectedAttribute.AttributeType)
            {
                case AttributeTypeCode.DateTime:
                    tmp = String.Format("datetime'{0}'", DateTime.Parse(Filter.Value).ToString("yyyy-MM-ddThh:mm:ss"));
                    break;
                case AttributeTypeCode.Lookup:
                case AttributeTypeCode.Customer:
                case AttributeTypeCode.Uniqueidentifier:
                case AttributeTypeCode.Owner:
                    tmp = String.Format("guid'{0}'", Filter.Value);
                    break;
               case AttributeTypeCode.String:
               case AttributeTypeCode.Memo:
                    tmp = String.Format("'{0}'", Filter.Value);
                    break;
            }

            return tmp;


        }
        
        public String ODataQuery
        {
            get { return this._ODataQuery; }
            set 
            {
                if (_ODataQuery != value)
                {
                    _ODataQuery = value;

                    QueryLength = String.Format("Length: {0}/2048", _ODataQuery.Length);

                    // Validate OData
                    if (_ODataQuery.Length > 2048)
                    {
                        IsAllowedToExecuteREST = false;
                    }
                    else
                    {
                        IsAllowedToExecuteREST = true;
                    }
                    IsAllowedToOpenBrowser = false;

                    OnPropertyChanged(() => IsAllowedToExecuteREST);
                    OnPropertyChanged(() => IsAllowedToOpenBrowser);
                    OnPropertyChanged(() => QueryLength); 
                    OnPropertyChanged(() => ODataQuery);
                    
                }
            }
        }
        public int? Top
        {
            get { return _top; }
            set
            {
                _top = value;
            }
        }
        public int? SkipNext
        {
            get { return _skipNext; }
            set
            {
                _skipNext = value;
            }
        }




        private AtomRequestCreator RequestCreatorATOM;
        private JSONRequestCreator RequestCreatorJSON;

         //private void TestQueryATOM(IDispatcherService Dispatcher, String Query)
         //{
         //    RequestCreatorATOM = new AtomRequestCreator();
         //    Uri currentURI = new Uri(Query, UriKind.RelativeOrAbsolute);
         //    WebRequest GETRequest = RequestCreatorATOM.CreateGET(currentURI);

         //    GETRequest.BeginGetResponse(
         //        (asResult) =>
         //        {
         //            Dispatcher.BeginInvoke((Action)delegate
         //            {
         //                try
         //                {
         //                    WebResponse GETResponse = GETRequest.EndGetResponse(asResult);
                            
         //                    ResultXml = "Test Successful";
         //                    IsAllowedToOpenBrowser = true;
         //                    OnPropertyChanged(() => ResultXml);
         //                    OnPropertyChanged(() => IsAllowedToOpenBrowser);
         //                }
         //                catch (WebException ex)
         //                {
         //                    ResultXml = "Test Unsuccessful";
         //                    IsAllowedToOpenBrowser = false;
         //                    OnPropertyChanged(() => ResultXml);
         //                    OnPropertyChanged(() => IsAllowedToOpenBrowser);
         //                }
         //            }
         //                , null);
         //        }, null);
         //}


         private void DownloadRawContentATOM(IDispatcherService Dispatcher, String ServiceURL, String Query)
         {
             RequestCreatorATOM = new AtomRequestCreator();

             Uri currentURI = new Uri(Query, UriKind.RelativeOrAbsolute);
             WebRequest GETRequest = RequestCreatorATOM.CreateGET(currentURI);

             GETRequest.BeginGetResponse(
                 (asResult) =>
                 {
                     Dispatcher.BeginInvoke((Action)delegate
                         {
                             try
                             {
                                 WebResponse GETResponse = GETRequest.EndGetResponse(asResult);
                                 List<KeyValuePair<string, string>> Headers = new List<KeyValuePair<string, string>>();
                                 foreach (var header in GETResponse.Headers)
                                 {
                                     Headers.Add(new KeyValuePair<string, string>(header.ToString(), GETResponse.Headers[header.ToString()].ToString()));
                                 }

                                 StreamReader textResponseReader = new StreamReader(GETResponse.GetResponseStream());

                                 ATOMResultXml.Append(textResponseReader.ReadToEnd());
                                 

                                 ResultXml = "Successful";
                                 IsAllowedToOpenBrowser = true;
                                 OnPropertyChanged(() => ResultXml);
                                 OnPropertyChanged(() => IsAllowedToOpenBrowser);
                                 OnPropertyChanged(() => ATOMResultXml);
                             }
                             catch (WebException ex)
                             {
                                 ATOMResultXml.Append(ex.Message + "\r\n");
                                 ResultXml = "Unsuccessful";
                                 IsAllowedToOpenBrowser = true;
                                 OnPropertyChanged(() => ResultXml);
                                 OnPropertyChanged(() => IsAllowedToOpenBrowser);
                                 OnPropertyChanged(() => ATOMResultXml);
                             }
                         }
                         , null);
                 }, null);
         }


         private void DownloadRawContentJSON(IDispatcherService Dispatcher, String ServiceURL, String Query)
         {
             RequestCreatorJSON = new JSONRequestCreator();

             Uri currentURI = new Uri(Query, UriKind.RelativeOrAbsolute);
             WebRequest GETRequest = RequestCreatorJSON.CreateGET(currentURI);

             GETRequest.BeginGetResponse(
                 (asResult) =>
                 {
                     Dispatcher.BeginInvoke((Action)delegate
                     {
                         try
                         {
                             WebResponse GETResponse = GETRequest.EndGetResponse(asResult);
                             List<KeyValuePair<string, string>> Headers = new List<KeyValuePair<string, string>>();
                             foreach (var header in GETResponse.Headers)
                             {
                                 Headers.Add(new KeyValuePair<string, string>(header.ToString(), GETResponse.Headers[header.ToString()].ToString()));
                             }

                             StreamReader textResponseReader = new StreamReader(GETResponse.GetResponseStream());
                             JSONResultXml.Append(textResponseReader.ReadToEnd());

                             ResultXml = "Successful";
                             IsAllowedToOpenBrowser = true;
                             OnPropertyChanged(() => ResultXml);
                             OnPropertyChanged(() => IsAllowedToOpenBrowser);
                             OnPropertyChanged(() => JSONResultXml);
                         }
                         catch (Exception ex)
                         {
                             JSONResultXml.Append(ex.Message + "\r\n");

                             ResultXml = "Unsuccessful";
                             IsAllowedToOpenBrowser = true;
                             OnPropertyChanged(() => ResultXml);
                             OnPropertyChanged(() => IsAllowedToOpenBrowser);
                             OnPropertyChanged(() => JSONResultXml);
                         }
                     }
                         , null);
                 }, null);
         }



         public void ExecuteRESTServiceATOM(IDispatcherService Dispatcher, String Query)
         {
             //TestQueryATOM(Dispatcher, Query);


             DownloadRawContentATOM(Dispatcher, ClientScript.GetServerUrl() + "/xrmservices/2011/OrganizationData.svc", Query);

         }

         public void ExecuteRESTServiceJSON(IDispatcherService Dispatcher, String Query)
         {

             DownloadRawContentJSON(Dispatcher, ClientScript.GetServerUrl() + "/xrmservices/2011/OrganizationData.svc", Query);

         }







        #endregion



        #region ICommands

        public ICommand Generate_Click
        {
            get
            {
                if (null == _generate_Click)
                {
                    _generate_Click = new MEFedMVVM.Common.DelegateCommand<object>((o) =>
                    {
                        ResultXml = "";
                        OnPropertyChanged(() => ResultXml);
                        
                        if(SelectedEntity != null)
                            ODataQuery = String.Format("{0}{1}{2}", ClientScript.GetServerUrl(), "/xrmservices/2011/OrganizationData.svc",  BuildODataQuery());


                    });
                }
                return _generate_Click;
            }
        }

        public ICommand Execute_Click
        {
            get
            {
                if (null == _execute_Click)
                {
                    _execute_Click = new MEFedMVVM.Common.DelegateCommand<object>((o) =>
                    {
                        
                        //TestQueryATOM(_dispatcher, ODataQuery); // Perform Test


                        ATOMResultXml.Clear();
                        JSONResultXml.Clear();
                        ExecuteRESTServiceATOM(_dispatcher, ODataQuery); // Perform Test
                        ExecuteRESTServiceJSON(_dispatcher, ODataQuery);
              
                    });
                }
                return _execute_Click;
            }
        }

        public ICommand OpenBrowserWithQuery_Click
        {
            get
            {
                if (null == _openBrowser_Click)
                {
                    _openBrowser_Click = new MEFedMVVM.Common.DelegateCommand<object>((o) =>
                    {

                        HtmlPage.Window.Navigate(new Uri(ODataQuery), "_blank");
              
                    });
                }
                return _openBrowser_Click;
            }
        }


       

        public ICommand AddGroupAND_Click
        {
            get
            {
                if (null == _addGroupAND_Click)
                {
                    _addGroupAND_Click = new MEFedMVVM.Common.DelegateCommand<object>((o) =>
                    {
                        CreateFilterGroupFromSelectedFilters("AND");    
                    });

                }
                return _addGroupAND_Click;
            }

        }

        public ICommand AddGroupOR_Click
        {
            get
            {
                if (null == _addGroupOR_Click)
                {
                    _addGroupOR_Click = new MEFedMVVM.Common.DelegateCommand<object>((o) =>
                    {
                        CreateFilterGroupFromSelectedFilters("OR");
                    });
                }
                return _addGroupOR_Click;
            }
        }


        private void CreateFilterGroupFromSelectedFilters(String GroupType)
        {
             List<FilterClass> group = null;

            // Make sure total selected is more then 1
            int count = 0;
            foreach (FilterClass item in FiltersList)
            {
                if (item.IsSelected)
                    count++;
            }

            if (count > 1)
            {
                List<FilterClass> listFilters = new List<FilterClass>();
                listFilters.AddRange(FiltersList);

                foreach (FilterClass item in listFilters)
                {
                    if (item.IsSelected)
                    {
                        item.IsSelected = false;
                        if (group == null)
                        {
                            item.IsPrimaryGroupAttribute = true;
                            group = new List<FilterClass>();
                        }

                        // Set filter as final group attribute if it is the last in the group to be added.
                        if (count == group.Count + 1)
                        {
                            item.IsFinalGroupAttribute = true;
                        }

                        item.GroupLogicalOperator = GroupType;
                        group.Add(item);

                        if(item.IsPrimaryGroupAttribute == false)
                        {
                            var indexOfPreviousItem = FiltersList.IndexOf(group[group.Count-2]);
                            if (FiltersList.IndexOf(item) != (indexOfPreviousItem + 1))
                            {
                                // Need to work out how to remove and add item back, collection doesn't like it..
                                FiltersList.Remove(item);
                                FiltersList.Insert(indexOfPreviousItem+1, item);
                            }
                        }
                    }
                }
                if (_entity.FilterGroups == null)
                    _entity.FilterGroups = new List<List<FilterClass>>();

                _entity.FilterGroups.Add(group);

                OnPropertyChanged(() => FiltersList);

            }
        }


        #endregion

    }


    
    /// <summary>
    /// A small class for managing exceptions.  Subscribe to the
    /// OnMessageAdded handler for actions when messages are added.
    /// </summary>
    public class ControlledExceptionMessages
    {
        private List<string> _exceptionMessages;
               
        public ControlledExceptionMessages()
        {
            _exceptionMessages = new List<string>();
        }

        /// <summary>
        /// Fires when the message collection changes.
        /// </summary>
        public EventHandler OnMessageAdded;

        public ObservableCollection<string> ExceptionMessages
        {
            get { return new ObservableCollection<string>(_exceptionMessages); }
        }

        public void Add(string message)
        {
            _exceptionMessages.Add(message);
            OnMessageAdded(this, null);
        }

        public void Clear()
        {
            _exceptionMessages.Clear();
            OnMessageAdded(this, null);
        }
    }


    #region Object Classes


    public class EntityClass : NotifyPropertyChangedBase
    {
        public EntityClass()
        {
            this.Attributes = new ObservableCollection<AttributeClass>();
            this.SortByAttributes = new ObservableCollection<AttributeClass>();
            this.FilterAttributes = new ObservableCollection<AttributeClass>();
            this.Filters = new ObservableCollection<FilterClass>();
            this.Relationships_OneToMany = new ObservableCollection<RelationshipClass>();
            this.Relationships_ManyToOne = new ObservableCollection<RelationshipClass>();
            this.Relationships_ManyToMany = new ObservableCollection<RelationshipClass>();
            InitialiseEvents();
        }            

        public EntityClass(String SchemaNameSet, String LogicalName)
        {
            this.SchemaNameSet = SchemaNameSet;
            this.LogicalName = LogicalName;
            this.Attributes = new ObservableCollection<AttributeClass>();
            this.SortByAttributes = new ObservableCollection<AttributeClass>();
            this.FilterAttributes = new ObservableCollection<AttributeClass>();
            this.Filters = new ObservableCollection<FilterClass>();
            this.Relationships_OneToMany = new ObservableCollection<RelationshipClass>();
            this.Relationships_ManyToOne = new ObservableCollection<RelationshipClass>();
            this.Relationships_ManyToMany = new ObservableCollection<RelationshipClass>();
            InitialiseEvents();
            
        }

        public string SchemaNameSet { get; set; }
        public string LogicalName { get; set; }
        private bool _isSelectAllAttributes;

        private ObservableCollection<AttributeClass> _attributes; 
        private ObservableCollection<AttributeClass> _sortByAttributes;
        private ObservableCollection<AttributeClass> _filterAttributes;

      
        private ObservableCollection<RelationshipClass> _relationships_OneToMany;
        private ObservableCollection<RelationshipClass> _relationships_ManyToOne;
        private ObservableCollection<RelationshipClass> _relationships_ManyToMany;
        private ObservableCollection<FilterClass> _filters;

        public List<List<FilterClass>> FilterGroups { get; set; }


        public event EventHandler AllAttributesSelectedEvent; // Denotes when Select all is selected
        public event EventHandler AllAttributesUnSelectedEvent; // Denotes when Select All is unselected
        public event EventHandler AnyAttributeUnSelectedEvent; // Denotes when any attribute is unselected

        private void InitialiseEvents()
        {
            //
            // Initialise Attribute selected events
            //
            _attributes.CollectionChanged += ((o, a) =>
            {
                NotifyCollectionChangedEventArgs args = (NotifyCollectionChangedEventArgs)a;

                if (args.Action == NotifyCollectionChangedAction.Add)
                {
                    AttributeClass item = (AttributeClass)args.NewItems[0];
                    item.IsAnyUnSelectedEvent += new EventHandler(this.UnSelectAnyAttribute);
                    item.IsSelectAllEvent += new EventHandler(this.SelectAllAttributes);
                    item.IsUnSelectAllEvent += new EventHandler(this.SelectAllAttributes);
                }
            });

            //
            // Initialise Filter Attribute selected events
            //
            _filters.CollectionChanged += ((o, a) =>
            {
                NotifyCollectionChangedEventArgs args = (NotifyCollectionChangedEventArgs)a;

                if (args.Action == NotifyCollectionChangedAction.Add)
                {
                    FilterClass item = (FilterClass)args.NewItems[0];
                    item.FilterAttributeSelectedEvent += new EventHandler(this.FilterAttributeSelected);
                    item.DeleteFilterEvent += new EventHandler(this.DeleteFilterRow);
                    item.UnGroupFiltersEvent += new EventHandler(this.UngroupFilters);
                }
            });

           

        }

        /// <summary>
        /// Performs action to add new Filter item row when last added filter row attribute selected
        /// </summary>
        /// <param name="source"></param>
        /// <param name="args"></param>
        public void FilterAttributeSelected(object source, EventArgs args)
        {
            if (source != null)
            {
                FilterClass item = (FilterClass)source;
                //
                // Add next filter but limit to 6 only
                if (this._filterAttributes.Count > 0 && Filters.Count < 6 && item == _filters[_filters.Count-1] )
                {
                    FilterClass filter = new FilterClass();
                    filter.Attributes = new AttributeClass[_filterAttributes.Count];
                    _filterAttributes.CopyTo(filter.Attributes, 0);
                    Filters.Add(filter);
                    OnPropertyChanged(() => Filters);
                }
            }
        }

         /// <summary>
        /// Performs action to add new Filter item row when last added filter row attribute selected
        /// </summary>
        /// <param name="source"></param>
        /// <param name="args"></param>
        public void DeleteFilterRow(object source, EventArgs args)
        {
            if (source != null)
            {
                FilterClass item = (FilterClass)source;
                Filters.Remove(item);
                OnPropertyChanged(() => Filters);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="source"></param>
        /// <param name="args"></param>
        public void UngroupFilters(object source, EventArgs args)
        {
            if (source != null)
            {
               
                FilterClass item = (FilterClass)source;
                List<FilterClass> tmpFilterGroup = null;

                foreach (List<FilterClass> group in FilterGroups)
                {
                    if (group[0] == item)
                    {
                        foreach (FilterClass filter in group)
                        {
                            filter.IsPrimaryGroupAttribute = false;
                            filter.IsFinalGroupAttribute = false;
                            filter.GroupLogicalOperator = null;
                        }
                        tmpFilterGroup = group;
                        break;
                    }
                }
                if (tmpFilterGroup != null)
                {
                    FilterGroups.Remove(tmpFilterGroup);
                    OnPropertyChanged(() => Filters);
                }
            }
        }

        /// <summary>
        /// Handles when any Attribute is unselected, except (Select All)
        /// </summary>
        /// <param name="source"></param>
        /// <param name="args"></param>
        public void UnSelectAnyAttribute(object source, EventArgs args)
        {
            if(IsSelectAllAttributes == true)
            {
                IsSelectAllAttributes = false;
                AnyAttributeUnSelectedEvent(this, null);
            }
        }

        /// <summary>
        /// Handles when (Select All) attributes is selected
        /// </summary>
        /// <param name="source"></param>
        /// <param name="args"></param>
        public void SelectAllAttributes(object source, EventArgs args)
        {
            if (IsSelectAllAttributes == false)
            {
                IsSelectAllAttributes = true;
                AllAttributesSelectedEvent(this, null);
            }
            else
            {
                IsSelectAllAttributes = false;
                AllAttributesUnSelectedEvent(this, null);
            }        
        }


        public bool IsSelectAllAttributes
        {
            get { return _isSelectAllAttributes; }
            set { if (_isSelectAllAttributes != value) { _isSelectAllAttributes = value; OnPropertyChanged(() => IsSelectAllAttributes); } }
        }

        public ObservableCollection<AttributeClass> Attributes
        {
            get { return _attributes; }
            set { if (_attributes != value) { _attributes = value; OnPropertyChanged(() => Attributes); } }
        }
        public ObservableCollection<AttributeClass> SortByAttributes
        {
            get { return _sortByAttributes; }
            set { if (_sortByAttributes != value) { _sortByAttributes = value; OnPropertyChanged(() => SortByAttributes); } }
        }
        public ObservableCollection<AttributeClass> FilterAttributes
        {
            get { return _filterAttributes; }
            set { if(_filterAttributes != value) { _filterAttributes = value; OnPropertyChanged(() => FilterAttributes); } }
        }
        public ObservableCollection<FilterClass> Filters
        {
            get { return _filters; }
            set { if (_filters != value) { _filters = value; OnPropertyChanged(() => Filters); } }
        }
        public ObservableCollection<RelationshipClass> Relationships_OneToMany
        {
            get { return _relationships_OneToMany; }
            set { if (_relationships_OneToMany != value) { _relationships_OneToMany = value; OnPropertyChanged(() => Relationships_OneToMany); } }
        }

        public ObservableCollection<RelationshipClass> Relationships_ManyToOne
        {
            get { return _relationships_ManyToOne; }
            set { if (_relationships_ManyToOne != value) { _relationships_ManyToOne = value; OnPropertyChanged(() => Relationships_ManyToOne); } }
        }

        public ObservableCollection<RelationshipClass> Relationships_ManyToMany
        {
            get { return _relationships_ManyToMany; }
            set { if (_relationships_ManyToMany != value) { _relationships_ManyToMany = value; OnPropertyChanged(() => Relationships_ManyToMany); } }
        }       

    }


    public partial class AttributeClass : NotifyPropertyChangedBase
    {
        public AttributeClass(AttributeMetadata Attribute)
        {
            this.Metadata = Attribute;
            
        }

        //public AttributeClass(String SchemaName, AttributeTypeCode? Type, String EntityLogicalName )
        //{
        //    this.SchemaName = SchemaName;
        //    this.Type = Type;
        //    this.ParentEntityName = EntityLogicalName;
        //}


        public AttributeMetadata Metadata { get; set; }
        public string SchemaName { get { return Metadata.SchemaName; } }
        public AttributeTypeCode? AttributeType { get { return Metadata.AttributeType; } }
        public string EntityLogicalName { get { return Metadata.EntityLogicalName; } }
        
        public event EventHandler IsAnyUnSelectedEvent;
        public event EventHandler IsSelectAllEvent;
        public event EventHandler IsUnSelectAllEvent;

        private bool _isSelected;

        public string UserLocalizedLabel 
        { 
            get 
            {
                string text = "";
                if((Metadata.DisplayName != null))
                {
                    text = (Metadata.DisplayName.UserLocalizedLabel != null) ? Metadata.DisplayName.UserLocalizedLabel.Label : Metadata.SchemaName;
                }
                else
                {
                    text = Metadata.SchemaName;
                }

                return text; 
            }
        }

        /// <summary>
        /// Allows for the selected option to be set without firing an event. (used for Select All action),
        /// </summary>
        public bool UpdateSelected
        {
            get { return _isSelected; }
            set { _isSelected = value; OnPropertyChanged(() => IsSelected); }
        }

        /// <summary>
        /// Is Selected property that fires events
        /// </summary>
        public bool IsSelected 
        {
            get { return _isSelected; }
            set
            {
                _isSelected = value;
                OnPropertyChanged(() => IsSelected);
                if (_isSelected == false)
                {
                    if (SchemaName == "(Select All)")
                    {
                        IsUnSelectAllEvent(this, null);
                    }
                    else
                    {
                        IsAnyUnSelectedEvent(this, null);
                    }                
                }
                else
                {
                    if (SchemaName == "(Select All)")
                    {
                        IsSelectAllEvent(this, null);
                    }
                }
            } 
        }

        

       
    }

    public class RelationshipClass : NotifyPropertyChangedBase
    {
        public RelationshipClass(String SchemaName, String EntityLogicalName)
        {
            this.SchemaName = SchemaName;
            this.EntityLogicalName = EntityLogicalName;
           
        }

        public string SchemaName { get; set; }
        public string EntityLogicalName { get; set; }
        public bool IsSelectAllAttributes { get; set; }
        private EntityClass _entity;

        public EntityClass Entity
        {
            get { return _entity; }
            set
            {
                _entity = value;
                OnPropertyChanged(() => Entity);
            }
        }
    }

    public enum SortDirection
    {
        asc,
        desc
    }

    //public enum FilterOperator
    //{
    //    eq,
    //    ne,
    //    gt,
    //    ge,
    //    lt,
    //    le,
    //    startswith,
    //    substringof,
    //    endswith

    //}

    //public enum LogicalOperator
    //{
    //    and,
    //    or        
    //}

    public class FilterClass: NotifyPropertyChangedBase
    {
        public FilterClass()
        {
            LogicalOperator = "AND";
            LogicalOperators = new string[2];
            LogicalOperators[0] = "AND";
            LogicalOperators[1] = "OR";           
        }
        
        public AttributeClass[] Attributes { get; set; }
        public string Value { get; set; }
        private string _filterOperator { get; set; }
        public string LogicalOperator { get; set; }
        public string[] LogicalOperators { get; set; }
        public bool IsSelected { get; set; }
        public bool IsPrimaryGroupAttribute { get; set; }
        public bool IsFinalGroupAttribute { get; set; }
        public string _groupLogicalOperator;
    
   

        public event EventHandler FilterAttributeSelectedEvent;
        public event EventHandler DeleteFilterEvent;
        public event EventHandler UnGroupFiltersEvent;

        public AttributeClass SelectedAttribute
        {
            get { return _attributeSelected; }
            set 
            { 
                if(_attributeSelected != value)
                {
                    _attributeSelected = value;
                    Value = "";

                    if (_attributeSelected != null)
                    {
                        FilterOperators = new ObservableCollection<string>();

                        FilterOperators.Add("Equals");
                        FilterOperators.Add("Does Not Equal");

                        switch(_attributeSelected.AttributeType.Value)
                        {
                            case AttributeTypeCode.String:
                            case AttributeTypeCode.Memo:
                                FilterOperators.Add("Contains");
                                FilterOperators.Add("Does Not Contain");
                                FilterOperators.Add("Begins With");
                                FilterOperators.Add("Does Not Begin With");
                                FilterOperators.Add("Ends With");
                                FilterOperators.Add("Does Not End With");
                                break;
                            case AttributeTypeCode.Decimal:
                            case AttributeTypeCode.Double:
                            case AttributeTypeCode.Integer:
                            case AttributeTypeCode.Money:
                                FilterOperators.Add("Is Greater Than");
                                FilterOperators.Add("Is Greater That or Equal To");
                                FilterOperators.Add("Is Less Than");
                                FilterOperators.Add("Is Less Than or Equal To");
                                break;
                            case AttributeTypeCode.DateTime:
                                FilterOperators.Add("On Or After");
                                FilterOperators.Add("On Or Before");
                                break;
                           
                            default:
                                break;
                        }
                        FilterOperators.Add("Contains Data");
                        FilterOperators.Add("Does Not Contain Data");


                       
                        FilterAttributeSelectedEvent(this, null);

                    }
                    FilterOperator = "Equals"; //set default
                    

                    OnPropertyChanged(() => SelectedAttribute);
                    OnPropertyChanged(() => FilterOperators);
                    OnPropertyChanged(() => FilterOperator);
                    OnPropertyChanged(() => LogicalOperators);
                    OnPropertyChanged(() => LogicalOperator);
                    OnPropertyChanged(() => AttributeListVisibilityLevel);
                    OnPropertyChanged(() => SelectFilterVisibilityLevel);
                    OnPropertyChanged(() => TextValueVisibilityLevel);
                    OnPropertyChanged(() => OptionSetValueVisibilityLevel);
                }
            }

        } private AttributeClass _attributeSelected;

       
        ObservableCollection<AttributeTextValue> _attributeValues;
        public ObservableCollection<AttributeTextValue> AttributeValues
        {
            get
            {
                if (_attributeSelected != null)
                {
                    if (_attributeValues == null)
                        _attributeValues = new ObservableCollection<AttributeTextValue>();
                    else
                        _attributeValues.Clear();

                    switch (_attributeSelected.AttributeType)
                    {
                        case AttributeTypeCode.Boolean:
                            BooleanAttributeMetadata b = (BooleanAttributeMetadata)_attributeSelected.Metadata;
                            _attributeValues.Add(new AttributeTextValue("false", b.OptionSet.FalseOption.Label.UserLocalizedLabel.Label));
                            _attributeValues.Add(new AttributeTextValue("true", b.OptionSet.TrueOption.Label.UserLocalizedLabel.Label));
                            break;
                        case AttributeTypeCode.Picklist:
                            PicklistAttributeMetadata p = (PicklistAttributeMetadata)_attributeSelected.Metadata;
                            foreach (OptionMetadata o in p.OptionSet.Options)
                                _attributeValues.Add(new AttributeTextValue(o.Value.ToString(), o.Label.UserLocalizedLabel.Label));
                            break;
                        case AttributeTypeCode.State:
                            StateAttributeMetadata state = (StateAttributeMetadata)_attributeSelected.Metadata;
                            foreach (OptionMetadata o in state.OptionSet.Options)
                                _attributeValues.Add(new AttributeTextValue(o.Value.ToString(), o.Label.UserLocalizedLabel.Label));
                            break;
                        case AttributeTypeCode.Status:
                            StatusAttributeMetadata status = (StatusAttributeMetadata)_attributeSelected.Metadata;
                            foreach (OptionMetadata o in status.OptionSet.Options)
                                _attributeValues.Add(new AttributeTextValue(o.Value.ToString(), o.Label.UserLocalizedLabel.Label));
                            break;


                    }
                }
                return _attributeValues;          
            }

        }
        

        public ObservableCollection<string> FilterOperators
        {
            get { return _filterOperators; }
            set { if (_filterOperators != value) { _filterOperators = value; OnPropertyChanged(() => FilterOperators); OnPropertyChanged(() => FilterOperator); } }
        
        } private ObservableCollection<string> _filterOperators;

        public string FilterOperator 
        {
            get { return _filterOperator;}
            set
            {
                _filterOperator = value; 
                OnPropertyChanged(() => TextValueVisibilityLevel);
                OnPropertyChanged(() => OptionSetValueVisibilityLevel);
            } 
        }


        public string AttributeListVisibilityLevel
        {
            get { return (SelectedAttribute == null) ? "Visible": "Collapsed"; }
        }

        public string SelectFilterVisibilityLevel
        {
            get { return (SelectedAttribute != null) ? "Visible": "Collapsed"; }
        }

        public string FilterActionsVisibilityLevel
        {
            get{  return (FilterActions != null) ? "Visible": "Collapsed"; }
        }

        public string LogicalOperatorVisibilityLevel
        {
            get { return (FilterActions == null) ? "Visible": "Collapsed"; }
        }

        public string OptionSetValueVisibilityLevel
        {
            get 
            { 
                string visible = "Collapsed";
                if (_filterOperator != "Contains Data" && _filterOperator != "Does Not Contain Data")
                {

                    if (_attributeSelected != null)
                    {
                        switch (_attributeSelected.AttributeType)
                        {
                            case AttributeTypeCode.Boolean:
                            case AttributeTypeCode.Picklist:
                            case AttributeTypeCode.State:
                            case AttributeTypeCode.Status:
                                visible = "Visible";
                                OnPropertyChanged(() => AttributeValues);
                                OnPropertyChanged(() => Value);
                                break;
                        }
                    }
                }
                return visible; 
            }
        }

        public string TextValueVisibilityLevel
        {
            get 
            { 
                string visible = "Collapsed";
                if (_filterOperator != "Contains Data" && _filterOperator != "Does Not Contain Data")
                {
                    if (_attributeSelected != null)
                    {

                        switch (_attributeSelected.AttributeType)
                        {
                            case AttributeTypeCode.BigInt:
                            case AttributeTypeCode.Customer:
                            case AttributeTypeCode.Decimal:
                            case AttributeTypeCode.Double:
                            case AttributeTypeCode.Integer:
                            case AttributeTypeCode.Lookup:
                            case AttributeTypeCode.ManagedProperty:
                            case AttributeTypeCode.Memo:
                            case AttributeTypeCode.Money:
                            case AttributeTypeCode.Owner:
                            case AttributeTypeCode.PartyList:
                            case AttributeTypeCode.String:
                            case AttributeTypeCode.Uniqueidentifier:
                            case AttributeTypeCode.Virtual:
                            case AttributeTypeCode.EntityName:
                            case AttributeTypeCode.DateTime:
                            case AttributeTypeCode.CalendarRules:
                                visible = "Visible";
                                OnPropertyChanged(() => Value);
                                break;
                        }
                    }
                }
                return visible; 
            }
        }

        public string[] FilterActions
        {
            get
            {
                if(IsPrimaryGroupAttribute)
                {
                    _filterActions = new string[2];
                    
                    _filterActions[0] = "Ungroup";
                    _filterActions[1] = "Delete";
                }
                else
                {
                    if(String.IsNullOrEmpty(GroupLogicalOperator))
                    {
                        _filterActions = new string[2];
                        if(IsSelected)
                            _filterActions[0] = "Deselect";
                        else
                            _filterActions[0] = "Select";

                        _filterActions[1] = "Delete"; 
                    }
                    else
                    {
                        _filterActions = null;
                    }
                   
                }
                return _filterActions;
            }

        } private string[] _filterActions;

        public string FilterAction
        {
            set
            {
                if (value != null && FilterAction != value)
                {
                   switch(value)
                   {
                        case "Select":
                            IsSelected = true;
                            OnPropertyChanged(() => IsSelected);
                            OnPropertyChanged(() => RowColour);
                            break;
                        case "Deselect":
                            IsSelected = false;
                            OnPropertyChanged(() => IsSelected);
                            OnPropertyChanged(() => RowColour);
                            break;
                        case "Ungroup":
                            UnGroupFiltersEvent(this, null);
                            break;
                        case "Delete":
                            DeleteFilterEvent(this, null);
                            break;
                    }

                   OnPropertyChanged(() => FilterAction);
                   OnPropertyChanged(() => FilterActions);
                }
                
            }
            get { return null; }
        }


        public string GroupLogicalOperator
        {
            set
            {
                if (_groupLogicalOperator != value)
                {
                    _groupLogicalOperator = value;

                    OnPropertyChanged(() => RowColour);
                    OnPropertyChanged(() => FilterActions);
                    OnPropertyChanged(() => FilterAction);
                    OnPropertyChanged(() => SelectedAttribute);
                    OnPropertyChanged(() => FilterOperators);
                    OnPropertyChanged(() => FilterOperator);
                    OnPropertyChanged(() => AttributeListVisibilityLevel);
                    OnPropertyChanged(() => SelectFilterVisibilityLevel);
                    OnPropertyChanged(() => FilterActionsVisibilityLevel);
                    OnPropertyChanged(() => LogicalOperatorVisibilityLevel);
                    OnPropertyChanged(() => GroupLogicalOperator);
                }
            }
            get
            {
                return _groupLogicalOperator;
            }
        }


        public string RowColour
        {
            get
            {
                return (IsSelected) ? "#FFBADDE9" : "White";
            }
        }

    }

    public class AttributeTextValue
    {
        public AttributeTextValue(string Value, string Name)
        {
            this.Value= Value;
            this.Name = Name;
        }
        public string Value {get;set;}
        public string Name {get;set;}
    }

    #endregion



//eq  Equal                 /AccountSet?$filter=Address1_City eq 'Redmond' 
//ne  Not equal             /AccountSet?$filter=Address1_City ne null 
//gt  Greater than          /AccountSet?$filter=CreditLimit/Value gt 1000 
//ge  Greater than or equal /AccountSet?&$filter=CreditLimit/Value ge 1000 
//lt  Less than             /AccountSet?$filter=CreditLimit/Value lt 1000 
//le  Less than or equal    /AccountSet?$filter=CreditLimit/Value le 1000 

//startswith  /AccountSet?$filter=startswith(Name, 'a') 
//substringof   /AccountSet?$filter=substringof('store',Name) 
//endswith     /AccountSet?$filter=endswith(Name, '(sample)') 
 


//and Logical and           /AccountSet?$filter=CreditLimit/Value ge 1000 and Address1_StateOrProvince eq 'TX' 
//or  Logical or            /AccountSet?$filter=AccountCategoryCode/Value eq 2 or AccountRatingCode/Value eq 1 
//not Logical Negation      /AccountSet?$filter=(AccountCategoryCode/Value ne null) and not (AccountCategoryCode/Value eq 1) 
 


}
